﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;

namespace Draco.OWF.Studio.Common.Geometry
{

    public enum LinesRelation
    {
        Parallel, Overlap, Intersection
    }
    /// <summary>
    /// LineSeg
    /// </summary>
    public class GeoLine
    {
        public Point Start { get; private set; }
        public Point End { get; private set; }
        public double EA { get; private set; }
        public double EB { get; private set; }

        public GeoLine(Point a, Point b)
        {
            if (a.X == b.X && a.Y == b.Y)
                throw new ArgumentException("start and end points can not be one point");
            Start = a;
            End = b;
            CaculateEquation();
        }
        /// <summary>
        /// Equation: ax+b=y
        /// </summary>
        private void CaculateEquation()
        {
            /**
             * line equation:  ax+b=y
             * Now we have two points:Start and End
             * So  
             *      ax+b=y
             *      ax'+b=y'
             * Now,we can caculate: a and b
             * a = (y'-y)/(x'-x)
             * b = y-ax
             * OK,we get a and b
             * Now we get equation: ax+b=y
             */
            if (this.Start.X == this.End.X)
            {
                this.EA = double.NaN;
                this.EB = double.NaN;
            }
            else if (this.Start.Y == this.End.Y)
            {
                this.EA = 0;
                this.EB = this.Start.Y;
            }
            else
            {
                this.EA = (this.End.Y - this.Start.Y) / (this.End.X - this.Start.X);
                this.EB = this.End.Y - this.EA * this.End.X;
            }
        }

        /// <summary>
        /// Equation: ax+b=y
        /// </summary>
        public virtual String Equation
        {
            get
            {
                if (this.EA == double.NaN)
                    return "x=" + this.Start.X;
                if (this.EA == 0)
                    return "y=" + this.Start.Y;
                return String.Format("{0}*x+{1}=y", this.EA, this.EB);
            }
        }

        public virtual bool IsPointOnLine(Point p)
        {
            if (this.EA == double.NaN)
            {
                return DoubleEq(p.X, this.Start.X);
            }
            if (this.EA == 0)
            {
                return DoubleEq(p.Y, this.Start.Y);
            }
            //ax+b=y
            return DoubleEq(this.EA * p.X + this.EB, p.Y);
        }

        public virtual LinesRelation GetRelationWithLine(GeoLine line)
        {
            if (DoubleEq(this.EA, line.EA) && DoubleEq(this.EB, line.EB))
                return LinesRelation.Overlap;
            if (DoubleEq(this.EA, line.EA))
                return LinesRelation.Parallel;
            return LinesRelation.Intersection;
        }

        public virtual Point? GetIntersectionPoint(GeoLine line)
        {
            if ( GetRelationWithLine(line) == LinesRelation.Intersection)
            {
                /**
                 *    ax+b=y  &&   a'x+b'=y
                 * => (a-a')x+(b-b')=0
                 * => x=(b'-b)/(a-a')
                 * */
                double x = (line.EB - this.EB) / (this.EA - line.EA);
                double y = this.EA * x + this.EB;
                return new Point(x,y);
            }
            return null;
        }

        private bool DoubleEq(double a, double b)
        {
            if (a == double.NaN && b == double.NaN)
                return true;
            if (a == double.NaN || b == double.NaN)
                return false;
            return Math.Abs(a - b) < 0.000001;
        }
    }
}
